#include "xMappingNode.h"
#include "xNodeType_StringComposer.h"
#include "xParameterPack.h"
#include "all_entries.h"
#include "xNodeType_WOP.h"
#include "xInMappingPort.h"
#include "xOutMappingPort.h"

xNodeType_StringComposer::xNodeType_StringComposer(){
	this->_wop = NULL;	
	this->_haveString = false;
	this->_name = "string_composer" ;
}

xNodeType_StringComposer::~xNodeType_StringComposer() {
}

void xNodeType_StringComposer::setupPorts(){
	assert(_parent != NULL);
	_parent->_inports.push_back(new xInMappingPort(_parent,"","tostring*",CLASS_REQUIRED,"NULL",CATEGORY_OTHER));
	_parent->_outports.push_back(new xOutMappingPort(_parent,"","string*",CLASS_REQUIRED,"NULL",CATEGORY_OTHER));
}

void xNodeType_StringComposer::createMappedObject(){
	xMappingNode *node = this->getParent();

	// Many objects may connect to this mapping node. 
	// Now, we collect all the objects and pack it into a xParameterPack object.
	_xpp.no = 0 ;
	for (int i = 0 ; i < node->getInportSize(); i++) {
		// get the nodetype pointer that connects to this inport (unique)

		xInMappingPort *inp = node->getInPort(i);
		xParameter xp = inp->toXParameter();
		_xpp.no ++;
		_xpp.paras.push_back(xp);
		if (inp->getLink() != NULL) _xpp.para_onoff.push_back(true);
		else _xpp.para_onoff.push_back(false);
	}

	string result = replaceCustomstring();
	assert(_wop == NULL);
	if(_wop == NULL){
		_wop = new WOP_String_Proxy(new WOP_String_Entry(result,NULL));
	}
	_wop->updateRealValue(result);
}

xParameter xNodeType_StringComposer::toXParameter(std::string type, xOutMappingPort *linked_op){
	xParameter xp ;
	if (type == "wop") {
		assert(false);
		xp.type = T_WOP ;
		xp.wop_nodetype = this ;
		xp.val_wop = _wop ;
	} else if (type == "varname") {
		xp.type = T_STRING ;
		xp.val_string = _wop->getName();
	} else if (type == "int*") {
		xp.type = TP_INT ;
		WOP_Int_Proxy * iwop = dynamic_cast<WOP_Int_Proxy*>(_wop);
		assert(iwop != NULL) ;
		xp.val_int_ptr = iwop->getIntValAddr();
	} else if (type == "int") {
		xp.type = T_INT ;
		WOP_Int_Proxy * iwop = dynamic_cast<WOP_Int_Proxy*>(_wop);
		assert(iwop != NULL) ;
		int *temp = iwop->getIntValAddr();
		xp.val_int = (*temp);
	} else if (type == "bool*") {
		xp.type = TP_BOOL ;
		WOP_Bool_Proxy * bwop = dynamic_cast<WOP_Bool_Proxy*>(_wop);
		assert(bwop != NULL) ;
		xp.val_bool_ptr = bwop->getBoolValAddr();
	} else if (type == "float*") {
		xp.type = TP_FLOAT ;
		WOP_Float_Proxy * bwop = dynamic_cast<WOP_Float_Proxy*>(_wop);
		assert(bwop != NULL) ;
		xp.val_float_ptr = bwop->getFloatValAddr();
	} else if (type == "double*") {
		xp.type = TP_DOUBLE ;
		WOP_Double_Proxy * bwop = dynamic_cast<WOP_Double_Proxy*>(_wop);
		assert(bwop != NULL) ;
		xp.val_double_ptr = bwop->getDoubleValAddr();
	} else if (type == "char*") {
		xp.type = TP_CHAR ;
		WOP_Char_Proxy * bwop = dynamic_cast<WOP_Char_Proxy*>(_wop);
		assert(bwop != NULL) ;
		xp.val_char_ptr = bwop->getCharValAddr();
	} else if (type == "ints") { 
		// this is the case where a composite VM asking for dimension
		assert(dynamic_cast<WOP_Array_Entry*>(_wop) != NULL) ;
		WOP_Array_Entry * ary = (WOP_Array_Entry*) _wop ;
		xp.type = T_INTS ;
		xp.val_ints = ary->getDimensionRange();
	} else if (type == "string") {
		xp.type = T_STRING ;
		xp.val_string = _wop->getVal();
	} else if (type == "string*") {
		xp.type = TP_STRING ;
		xp.val_string_ptr = _wop->getValAddr();
	} else if (type == "tostring*") {
		xp.type = TP_STRING ;
		WOP_String_Proxy * iwop = dynamic_cast<WOP_String_Proxy*>(_wop);
		assert(iwop != NULL) ;
		xp.val_string_ptr = iwop->getStrValAddr();
	} else 
		assert(false);
	// This assertion false occurs due to
	return xp;
}

bool xNodeType_StringComposer::update(){

	string result = replaceCustomstring();
	_wop->updateRealValue(result);
	return true;
}

bool xNodeType_StringComposer::haveCustomString(){
	return _haveString;
}

string xNodeType_StringComposer::isStringValidate(string customstring){
	string result = "TRUE";
	_customString = customstring;
	xMappingNode *node = this->getParent();
	int inportsize = node->getInportSize()-1;
	index.clear();
	splitString(customstring);
	
	int check;
	int i = 0;
	IndexCollect::iterator it;
	for(it = index.begin();it != index.end() ;it++ , i++){		
		stringstream oss;
		string tempstring;
		tempstring = (*it).substr(1);
		oss << tempstring;
		oss >> check;
		if(check != i){
			return "The index is overflow or underflow";
		}
	}

	_haveString = true;		//added by howway
	return result;
}

void xNodeType_StringComposer::splitString(string s){
	size_t first , last;
	first = 0;
	last = s.find("$");
	
	while(last != string::npos){
		first = last;
		last++;
		bool match = false;

		while ( last < s.length() )		// edited by howway
		{
			if(s[last] <=47 || s[last] >=58)
				break;
			
			match = true;
			last++;			
			
		}

		if(match){
			string sub = s.substr(first,last-first);
			index.insert(sub);		
		}
		last = s.find("$",first+1);		
	}
}

string xNodeType_StringComposer::replaceCustomstring(){
	string result;
	int uppersize = 0;
	if(_xpp.paras.size() >= index.size()){
		uppersize = index.size();
	}
	else{
		uppersize = _xpp.paras.size();
	}
	IndexCollect::iterator it;
	int i;
	size_t replace_index,space_index,start_index;
	for(i=0,it = index.begin() ;i<uppersize;i++ , it++){
		replace_index = _customString.find((*it));
		if(i == 0){
			start_index = 0;
		}		
		space_index = _customString.find(" ",replace_index+1);
		while(replace_index != string::npos){
			if(space_index != string::npos){
				result+=_customString.substr(start_index,replace_index-start_index);
				result+=*(_xpp.paras[i].val_string_ptr);
				start_index = replace_index+2;
				break;
			}
			else{
				result+=_customString.substr(start_index,replace_index-start_index);
				result+=*(_xpp.paras[i].val_string_ptr);
				start_index = replace_index+2;
				break;
			}

			replace_index = _customString.find((*it));
			space_index = _customString.find(" ",replace_index+1);
		}		
	}
	if(result.size() == 0){
		result = _customString;
	}
	return result;
}

string xNodeType_StringComposer::valid_rhs_pair[][2] = { // the left is the woptype of this node, right is the link that connects to
	"wop","tostring*",
	"int*","tostring*",
	"string*","tostring*",
	"dontcare","tostring*",
	"dontcare","string*"
};
bool xNodeType_StringComposer::verify_rhs_link(xOutMappingPort *left, xInMappingPort *right, xNodeType* rightnode) {

	if (_wop == NULL) { // in many cases, the mapping node may not have a _wop object yet
		// before the link is checked in this case, we check
		if (right->getType() == "tostring*")
			return true ;
	}
	string wop_type = _wop->getTypeStr() ;
	return check_valid_pair(valid_rhs_pair, sizeof(valid_rhs_pair)/(sizeof(string)*2),
		wop_type, right->getType());
}
string xNodeType_StringComposer::gate_pair[][2] = {
	"int*","tostring*",
	"wop","tostring*",
	"string*","tostring*",
	"dontcare","tostring*"
};
string xNodeType_StringComposer::wop_pair[][2] = { // the left is the woptype of this node, right is the link that connects to
	"int*","tostring*",
	"wop","tostring*",
	"string*","tostring*",
	"int","tostring*",
	"double*","tostring*",
	"double","tostring*",
	"string","tostring*",
	"bool","tostring*",
	"dontcare","int*",
	"dontcare","tostring*",
	"dontcare","string*",
	"dontcare","int",
	"dontcare","double",
	"dontcare","double*",
	"dontcare","bool"
};
string xNodeType_StringComposer::other_pair[][2] = {
	"dontcare","int*",
	"dontcare","tostring*",
	"dontcare","string*",
	"dontcare","int",
	"dontcare","double",
	"dontcare","double*",
	"dontcare","bool"
};

bool xNodeType_StringComposer::verify_lhs_link(xOutMappingPort *left, xNodeType* leftnode_nt, xInMappingPort *right) {
	if (leftnode_nt->getType() == "gate") {
		// if the lhs is a gate port, 
		return check_valid_pair(gate_pair,sizeof(gate_pair)/(sizeof(string)*2),
			"dontcare",left->getType());
	}
	if (leftnode_nt->getType() == "wop") { // if the leftnode is a wop, we want to check if
		//   its type is int,
		xNodeType_WOP *wopnt = (xNodeType_WOP*)(leftnode_nt);
		WOP_Entry *wop = wopnt->getWOP();
		return check_valid_pair(wop_pair,sizeof(wop_pair)/(sizeof(string)*2),
			"dontcare",wop->getTypeStr());
	}
	// to here left is often the outport of other mapping node
	// we limit those ports to be of type int*, double*,
	return check_valid_pair(other_pair,sizeof(other_pair)/(sizeof(string)*2),
		"dontcare",left->getType());
}
string xNodeType_StringComposer::replaceSpace(){
	string tempString = _customString;
	string::iterator it;
	for(it = tempString.begin(); it != tempString.end() ; ++it){
		if((*it) == ' '){
			(*it) = 1;
		}
	}
	return tempString;
}
string xNodeType_StringComposer::recoverSpace(string customstring){
	string tempString = customstring;
	string::iterator it;
	for(it = tempString.begin(); it != tempString.end() ; ++it){
		if((*it) == 1){
			(*it) = ' ';
		}
	}
	return tempString;
}
string xNodeType_StringComposer::toMDString(MDSType type){
	string mdsvalue = replaceSpace();
	ostringstream MDSstr ;

	if ( type == xNodeType::NormalMDSFormat ){
		MDSstr << "node "<<  _parent->getSerialNo() << " stringcomposer "<< "@ " << mdsvalue << " @" << endl ; 
	} else if ( type == xNodeType::ParameterizedMDSFormat ){
		MDSstr << "ADD_NODE " <<"node "<< "SN" <<_parent->getSerialNo() << " stringcomposer "<< "@ " << mdsvalue << " @" << endl ; 
	} else {
		assert(false);
	}
	
	return MDSstr.str();
}
int xNodeType_StringComposer::parseMDString(vector<string>& tokens, int start_index) {
	this->setInportFixed(false);
	this->setOutportFixed(true);

	int i = start_index ;
	assert(tokens[i] == "node");
	string serialno = tokens[i+1] ;
	assert(tokens[i+2] == "stringcomposer");
	assert(tokens[i+3] == "@");
	int k = 0 ;
	string customString = "" ;
	do { // a formula may have spaces  
		customString = customString + tokens[i+4+k];
		k++ ;
	} while (tokens[i+4+k] != "@");
	customString = recoverSpace(customString);
	this->isStringValidate(customString);
	this->getParent()->deletePort(0);
	return 5+k ; // five tokens are 
}

void xNodeType_StringComposer::removeCustomString()
{
	this->_customString = "";
	this->_haveString = false;
}

